// Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

#include "cmsis_os2.h"

#include <array>
#include <cassert>
#include <cinttypes>
#include <cstdarg>
#include <cstdio>
#include <cstring>

#define PW_LOG_MODULE_NAME "main"

#include "pw_assert/check.h"
#include "pw_log/log.h"
#include "pw_log_cmsis_driver/backend.h"

#include "iotsdk/ip_network_api.h"

#include "server.h"

extern "C" {
void pw_sys_io_init(ARM_DRIVER_USART *);
}

extern void serial_setup(std::array<ARM_DRIVER_USART *, 2> &);

static void main_task_cb(void *ignored)
{
    PW_LOG_INFO("Starting RPC server");
    start_rpc_app();
}

static void network_event_callback(const network_state_event_args_t *event_args)
{
    switch (event_args->event) {
        case NETWORK_UP:
            PW_LOG_INFO("Network Up");

            // Update the stored IP address
            set_ip_address(&(event_args->up_event_args.ip));

            // Start the RPC server thread the first time the network comes up
            {
                static osThreadId_t main_thread = 0;
                if (!main_thread) {
                    static const osThreadAttr_t thread_attr = {.stack_size = 4096};
                    main_thread = osThreadNew(main_task_cb, nullptr, &thread_attr);
                    PW_CHECK_NOTNULL(main_thread, "osThreadNew failed");
                }
            }
            break;
        case NETWORK_DOWN:
            PW_LOG_ERROR("Network Down");
            break;
        default:
            break;
    }
}

int main()
{
    // Initialise UART0 for logging and UART1 for RPC
    std::array<ARM_DRIVER_USART *, 2> serials;
    serial_setup(serials);

    osStatus_t res = osKernelInitialize();
    assert(res == osOK);

    // Initialise logging/RPC subsystems
    pw_log_cmsis_driver_init(serials[0]);
    pw_sys_io_init(serials[1]);

    // Start up network
    res = start_network_task(network_event_callback, 0);
    PW_CHECK_INT_EQ(res, osOK, "Failed to start_network_task");

    // Start kernel
    auto state = osKernelGetState();
    PW_CHECK_INT_EQ(state, osKernelReady, "kernel is not ready");

    PW_LOG_INFO("Starting kernel");

    res = osKernelStart();
    PW_CHECK_INT_EQ(res, osOK, "osKernelStart failed");

    PW_LOG_CRITICAL("osKernelStart should not have returned but did");
    PW_UNREACHABLE;
}
